
#pragma once
#include "common.h"
#include <functional>

//已删除部分注释

//输入事件和生成的通知事件，均使用局部坐标
enum UIEvent_t
{
	//鼠标按下，只处理左键
	//参数1：x
	//参数2：y
	EvtMouseDown,

	//鼠标抬起，只处理左键
	//参数1：x
	//参数2：y
	EvtMouseUp,

	//鼠标移动
	//参数1：x
	//参数2：y
	EvtMouseMove,

	//鼠标滚动
	//参数1：滚动次数（负数为上，正数为下）
	//参数2：无
	EvtMouseWheel,

	//键盘按下
	//参数1：按键
	//参数2：Ctrl是否按下
	EvtKeyDown,

	//键盘弹起
	//参数1：按键
	//参数2：Ctrl是否按下
	EvtKeyUp,

	//字符输入
	//参数1：字符串（WCHAR*，不含\0）
	//参数2：长度
	EvtKeyInput,

	//查询输入法光标位置
	//参数1：xptr
	//参数2：yptr
	//返回1代表更新指针，返回0代表没处理
	EvtQueryImmPos,

	//控件初始化通知（插入界面管理器才触发）
	//参数1：PageId
	//参数2：无
	EvtInit,

	//控件销毁通知
	//参数1：PageId
	//参数2：原因（0-主动移除，1-父控件被销毁，2-页被销毁，3-程序退出）
	//不再区分销毁原因，需要在代码多处标记不同原因，但是几乎没用
	EvtUninit,

	//鼠标进入区域通知
	//参数1：无
	//参数2：无
	EvtMouseHover,

	//鼠标离开区域通知
	//参数1：无
	//参数2：无
	EvtMouseLeave,

	//鼠标点击通知（鼠标抬起时在控件范围内才会发送）
	//参数1：无
	//参数2：无
	EvtClick,

	//控件获得输入焦点通知
	//参数1：无
	//参数2：无
	EvtFocus,

	//控件失去输入焦点通知
	//参数1：无
	//参数2：无
	EvtBlur,

	//鼠标拖动开始通知
	//参数1：鼠标按下时的x
	//参数2：鼠标按下时的y
	EvtDragStart,

	//鼠标拖动结束通知
	//参数1：鼠标抬起时的x
	//参数2：鼠标抬起时的y
	EvtDragEnd,



	//CheckBox状态变化
	//参数1：当前是否选中
	//参数2：无
	EvtCheckBoxChange,

	//滚动条通知
	//参数1：滚动的目标位置的1000倍（正常没法传递小数）
	//参数2：是否为横向
	EvtScrollTo,

	//控件选项发生变化，适用于DropList和ListBox
	//参数1：Index
	//参数2：是否选中（多选的ListBox可取消）
	EvtSelectChange,

	//滑动条滑块移动
	//参数1：对应值的1000倍（正常没法传递小数）
	//参数2：是否为横向
	EvtSliderChange,

	//特殊ListBox选项变化通知
	//参数1：已选中Id列表
	//参数2：已选中数量
	EvtSpSelectChange,

	//编辑框内容发生变化，失去焦点时才会发送（回车键会导致单行编辑框失去焦点）
	//参数1：当前文本指针
	//参数2：当前文本长度
	EvtEditChange,


};


//事件参数必须设为PVOID，否则在64位编译会截断传递的指针
class UIControl;
typedef int(*EventHandler_t)(UIControl* CtrlObj,UIEvent_t UIEvent,PVOID Param1,PVOID Param2);
typedef int(UIControl::*EventHandlerM_t)(UIControl* CtrlObj,UIEvent_t UIEvent,PVOID Param1,PVOID Param2);


//管理器-》control-》component-》control-》eventhandler
class UIComponent
{
public:
	int Id;
	int X;
	int Y;
	int Width;
	int Height;

	BOOL Enable;
	//目前以Visible控制组件是否使用，以后可能添加专用的Valid字段
	BOOL Visible;
	BOOL Pushed;
	BOOL Hovered;
	UIControl* Owner;

	UIComponent()
	{
		Id=-1;
		Enable=TRUE;
		Visible=TRUE;
		Pushed=FALSE;
		Hovered=FALSE;
	}

	virtual ~UIComponent(){}

	virtual void Draw()=0;
	virtual void Update(){}
	virtual int Notify(UIEvent_t UIEvent,PVOID Param1,PVOID Param2)=0;
};

enum ControlType_t
{
	CT_Empty,
	CT_Button,
	CT_Image,
	CT_Label,
	CT_CheckBox,
	CT_ScrollView,
	CT_Edit,
	CT_ListBox,
	CT_SpListBox,
	CT_DropList,
	CT_Slider,
	CT_Adjust,
	CT_TabView,
};

class UIControl
{
public:
	ControlType_t Type;
	//名称由子类设置和释放
	WCHAR* Name;
	//尽管父子关系存储在控件类内，但是完全由管理器控制
	UIControl* Parent;
	std::list<UIControl*> ChildList;

	//具体定义由子类自行处理，基类仅处理通用的鼠标事件
	//暂时限定最多4个组件，避免动态分配内存
	UIComponent* SubCpnt[4];
	int ComponentNum;

	//相对于父节点的位置
	int X;
	int Y;
	int Width;
	int Height;

	int ViewPosX;
	int ViewPosY;
	BOOL Scrollable;
	BOOL Visible;
	BOOL Enable;
	BOOL Pushed;
	BOOL Hovered;

	PVOID UserData;

	UIControl()
	{
		Name=NULL;
		Parent=NULL;
		ViewPosX=0;
		ViewPosY=0;
		Enable=TRUE;
		Visible=TRUE;
		Scrollable=FALSE;
		Pushed=FALSE;
		Hovered=FALSE;
		EventHandler=NULL;
		UserData=NULL;

		//SubCpnt=NULL;
		ComponentNum=0;
	}

	virtual ~UIControl()
	{

	}

	virtual void Draw()=0;
	virtual int Notify(UIEvent_t UIEvent,PVOID Param1,PVOID Param2)=0;
	virtual void Update(){}
	virtual int InternalNotify(int Index,UIEvent_t UIEvent,PVOID Param1,PVOID Param2){return 0;}

	BOOL IsVisible()
	{
		return this->Visible;
	}

	void SetVisible(BOOL Visible)
	{
		this->Visible=Visible;
	}

	BOOL IsEnable()
	{
		return this->Enable;
	}

	void SetEnable(BOOL Enable)
	{
		this->Enable=Enable;
	}

	PVOID GetUserData()
	{
		return this->UserData;
	}

	void SetUserData(PVOID UserData)
	{
		this->UserData=UserData;
	}

	//由于没提供SetSize()方法，不允许乱改控件大小

	//EventHandler只能设置一个，不支持多个监听者订阅同一事件，也不支持仅订阅某一事件
	std::function<int(UIControl*,UIEvent_t,PVOID,PVOID)> EventHandler;

	//全局函数和静态成员函数专用
	void SetEventHandler(EventHandler_t EventHandler)
	{
		this->EventHandler=EventHandler;
	}

	//成员函数专用（废弃）
	void SetEventHandler(UIControl* Obj,EventHandlerM_t EventHandler)
	{
		this->EventHandler=std::bind(EventHandler,Obj,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3,std::placeholders::_4);
	}

	//lambda表达式专用（VS2010不支持将无捕获的lambda表达式转换成函数指针）
	//较高版本的VS可以将lambda表达式强制转换，仅需保留一个重载，否则反而会报错
	//https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2012/hh567368(v=vs.110)#lambdas
	void SetEventHandler(std::function<int(UIControl*,UIEvent_t,PVOID,PVOID)> EventHandler)
	{
		this->EventHandler=EventHandler;
	}	
};





/*
UI管理器以page为单位划分，每次只显示一个page
每个page上可放置任意个控件，控件有父子关系，形成树状结构
page默认带有一个root节点，所有控件均直接或间接为root的子节点
root节点没有名字，外部不可访问，需要用到时以NULL指代
同一控件下的子节点，以加入先后顺序区分zorder，先加入在底层，后加入的在顶层
插入控件时，需要明确填写父节点，此后控件归管理器所有，使用者可以根据名字找到控件
移除控件时，其子节点也会递归移除，管理器负责释放内存

管理器负责每帧遍历控件，调用控件的Draw()和Update()方法
管理器接受窗口输入，根据各控件所在区域分发，由控件自身进行进一步处理
管理器负责维护输入状态，如焦点，鼠标悬浮和按下等，窗口激活/进入后台时会重置状态
所有控件均使用绝对定位，不支持自动布局，每个控件记录的坐标是相对其父节点的局部坐标
控件绘制和处理输入时，管理器会将窗口坐标映射至控件局部坐标

控件均为UIControl的子类，需要实现Draw()，Update()和Notify()三个接口
控件允许外部通过SetEventHandler()传入一个事件处理函数，接收相关通知
控件内部可以嵌入UIComponent对象，实现一些通用功能
*/
class GUIManager
{
private:
	struct PageData_t 
	{
		int Id;
		int Count;
		//int MaxDepth;
		UIControl* Root;	//创建时自动生成一个CEmpty
		std::map<WCHAR*,UIControl*,wstrless_t> NameMap;
	};

	int DbgRecursiveNum;
	std::map<int,PageData_t*> PageData;
	PageData_t* DisplayPage;

	//处理输入时，不允许删除Page和Control，否则会导致管理器引用失效
	//需记录下需要操作的内容，在输入处理结束后，一并执行实际操作（可能有别的更好的方案）
	BOOL InProcessState;
	int DeferDisplayPageId;
	//只有DisplayPage删除时才会触发延时，记录一个就够了
	int DeferDeletePageId;	
	BOOL PageWillChange;
	BOOL PageWillDelete;
	//只有DisplayPage删除时才会触发延时，记录一个就够了
	int DeferDeleteControlPageId;
	std::list<WCHAR*> DeferDeleteList;

	//管理器全局控件状态
	UIControl* HoveredCtrl;
	UIControl* PushedCtrl;
	UIControl* FocusCtrl;
	UIControl* TopmostCtrl;
	//UIControl* DraggedCtrl;	//不支持拖拽移动控件
	//管理器记录窗口坐标
	int MouseX;
	int MouseY;
	BOOL DragState;
	int DragBeginX;
	int DragBeginY;
	//Alt键太麻烦了不记录
	BOOL KeyCtrlDown;
	BOOL KeyShiftDown;
	//窗口尺寸变化的处理
	BOOL Freezed;
	int ClientWidth;
	int ClientHeight;

public:
	GUIManager();
	~GUIManager();
	void LoadFixResource();

public:
	//调用者需要自行保存返回的pageid
	int CreatePage();
	//销毁页面时，页面上的所有控件也会一并销毁
	void DestroyPage(int PageId);
	//如果没有设置显示页面，或显示页面被销毁，则什么都不会显示
	void SetDisplayPage(int PageId);
	int GetControlPageId(UIControl* Control);
private:
	PageData_t* FindPage(int PageId);

public:
	//加入管理器后，由管理器全权管理，调用者不再负责释放内存
	BOOL InsertControl(int PageId,UIControl* Parent,UIControl* Control);
	//删除控件时，会将其子控件一并删除
	BOOL DeleteControl(int PageId,WCHAR* Name);
	UIControl* FindControl(int PageId,WCHAR* Name);
private:
	void RecursiveDeleteControl(PageData_t* Page,UIControl* Control);
	UIControl* MouseHitTest();
	UIControl* ScrollHitTest();

public:
	void UpdateView();
	void UpdateFrame();
	void ResizeView(int Width,int Height,BOOL Minimized);
	int LastOriginX;
	int LastOriginY;
	//绘制过程中临时缩小区域，可用于子组件绘制
	void ShrinkDrawingRect(int Left,int Top,int Width,int Height);
	void RecoverDrawingRect();
private:
	void RecursiveDraw(UIControl* Current,int BasePosX,int BasePosY);

public:
	void ResetInputState();
	void DispatchInput(UINT WinMessage,PVOID Param1,PVOID Param2);
	int ComponentMouseDispatch(UIControl* Control,UIEvent_t UIEvent,PVOID Param1,PVOID Param2);
	BOOL TrySetTopmost(UIControl* Control,BOOL Enable);
	void BacktraceScreenPosition(UIControl* Control,int* PosX,int* PosY);
	BOOL IsCtrlKeyDown();
	void GetMousePosForControl(UIControl* Control,int* X,int* Y);
	void BlurControl(UIControl* Control);

	//void MsgBox(WCHAR*Text,WCHAR* Title,int Style);
private:
	void RecordInput(UINT WinMessage,PVOID Param1,PVOID Param2);
};